package com.mdd.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.mdd.admin.LikeAdminThreadLocal;
import com.mdd.admin.service.IOrderService;
import com.mdd.admin.validate.commons.PageValidate;
import com.mdd.admin.validate.order.OrderBatchValidate;
import com.mdd.admin.validate.order.OrderSearchValidate;
import com.mdd.common.core.PageResult;
import com.mdd.common.entity.coupon.Coupon;
import com.mdd.common.entity.order.*;
import com.mdd.common.entity.user.User;
import com.mdd.common.enums.CodeEnum;
import com.mdd.common.enums.OrderStatusEnum;
import com.mdd.common.mapper.order.*;
import com.mdd.common.mapper.user.UserMapper;
import com.mdd.common.util.AmountUtils;
import com.mdd.common.util.OrderUtils;
import com.mdd.common.util.TimeUtils;
import com.mdd.common.util.WxPayUtils;
import com.mdd.common.vo.OrderMealVo;
import com.mdd.common.vo.OrderVo;
import com.wechat.pay.java.service.refund.model.*;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.Cleanup;
import lombok.SneakyThrows;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.channels.InterruptedByTimeoutException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author shenjian
 * @createTime 2023年02月26日 13:23:00
 */
@Service
public class OrderServiceImpl implements IOrderService {

    private final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);

    @Resource
    UserMapper userMapper;
    @Resource
    OrderMapper orderMapper;
    @Resource
    CouponMapper couponMapper;
    @Resource
    RefundMapper refundMapper;
    @Resource
    AddressMapper addressMapper;
    @Resource
    MansionMapper mansionMapper;
    @Resource
    MenuMealMapper menuMealMapper;
    @Resource
    OrderMealMapper orderMealMapper;
    @Resource
    OrderMenuMapper orderMenuMapper;
    @Resource
    OrderDetailMapper orderDetailMapper;

    @SneakyThrows
    @Override
    public PageResult<OrderVo> list(OrderSearchValidate searchValidate) {
        Long deliveryStartTime;
        Long deliveryEndTime;
        Integer pageNo = searchValidate.getPageNo();
        Integer pageSize = searchValidate.getPageSize();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String deliveryTime = searchValidate.getDeliveryTime();
        if (StringUtils.isNotBlank(deliveryTime)) {
            deliveryStartTime = dateFormat.parse(deliveryTime + " 00:00:00").getTime() / 1000;
            deliveryEndTime = dateFormat.parse(deliveryTime + " 23:59:59").getTime() / 1000;
        } else {
            deliveryEndTime = null;
            deliveryStartTime = null;
        }
        String orderTime = searchValidate.getOrderTime();
        PageResult<OrderVo> pageResult = new PageResult<>();
        Page<OrderVo> pages = PageHelper.startPage(pageNo, pageSize).doSelectPage(() ->
                this.orderMapper.selectOrderListByWhere(searchValidate.getUserId(),
                        searchValidate.getMansionId(), searchValidate.getOrderNo(), searchValidate.getPhone(),
                        deliveryStartTime, deliveryEndTime, orderTime));
        List<Integer> userIds = pages.getResult().stream().map(OrderVo::getUserId).collect(Collectors.toList());
        pageResult.setPageNo(pages.getPageNum());
        pageResult.setPageSize(pages.getPageSize());
        pageResult.setCount(pages.getTotal());
        pageResult.setLists(pages.getResult());
        for (OrderVo order : pageResult.getLists()) {
            order.setStatusTranslate(OrderStatusEnum.getValueByCode(order.getStatus()));
            order.setActualAmount(AmountUtils.changeF2Y(order.getActualAmountF()));
            order.setCouponAmount(AmountUtils.changeF2Y(order.getCouponAmountF()));
            order.setTotalAmount(AmountUtils.changeF2Y(order.getTotalAmountF()));
            if (order.getCreateTime() != null) {
                order.setCreateTimeStr(TimeUtils.timestampToDate(order.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
            }
            if (order.getDeliveryTime() != null) {
                order.setDeliveryTimeStr(TimeUtils.timestampToDate(order.getDeliveryTime(), "yyyy-MM-dd HH:mm:ss"));
            }
            if (order.getPayTime() != null) {
                order.setPayTimeStr(TimeUtils.timestampToDate(order.getPayTime(), "yyyy-MM-dd HH:mm:ss"));
            }
            order.setActualAmountF(null);
            order.setCouponAmountF(null);
            order.setTotalAmountF(null);
            order.setCreateTime(null);
            order.setDeliveryTime(null);
            order.setPayTime(null);
        }
        return pageResult;
    }

    @Override
    public OrderVo getOrderDetail(Integer orderId) {
        OrderVo orderVo = new OrderVo();
        final Order order = this.orderMapper.selectOne(new QueryWrapper<Order>()
                .eq("id", orderId)
                .last("limit 1"));
        Assert.isTrue((order != null && order.getId() != null), "订单数据不存在");
        User user = this.userMapper.selectById(order.getUserId());
        orderVo.setId(order.getId());
        orderVo.setUserId(order.getUserId());
        orderVo.setUserSn(user.getSn());
        orderVo.setOrderNo(order.getOrderNo());
        orderVo.setStatus(order.getStatus());
        orderVo.setStatusTranslate(OrderStatusEnum.getValueByCode((order.getStatus())));
        // 地址
        Integer addressId = order.getAddressId();
        if (addressId != null) {
            Address address = this.addressMapper.selectById(addressId);
            if (address != null) {
                Mansion mansion = mansionMapper.selectById(address.getMansionId());
                if (mansion != null) {
                    orderVo.setMansionId(mansion.getId());
                    orderVo.setMansionName(mansion.getName());
                }
                orderVo.setNickName(address.getNickName());
                orderVo.setMobile(address.getMobile());
                orderVo.setFloor(address.getFloor());
                //拼接详细地址
                orderVo.setAddressDetail(address.getCompanyName() + " - " + address.getHouseNumber());
            }
        }
        orderVo.setMealNumber(order.getMealNumber());
        orderVo.setActualAmount(AmountUtils.changeF2Y(order.getActualAmount()));
        orderVo.setCouponAmount(AmountUtils.changeF2Y(order.getCouponAmount()));
        orderVo.setTotalAmount(AmountUtils.changeF2Y(order.getTotalAmount()));
        if (order.getCreateTime() != null) {
            orderVo.setCreateTimeStr(TimeUtils.timestampToDate(order.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
        }
        if (order.getDeliveryTime() != null) {
            orderVo.setDeliveryTimeStr(TimeUtils.timestampToDate(order.getDeliveryTime(), "yyyy-MM-dd HH:mm:ss"));
        }
        if (order.getPayTime() != null) {
            orderVo.setPayTimeStr(TimeUtils.timestampToDate(order.getPayTime(), "yyyy-MM-dd HH:mm:ss"));
        }
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(new QueryWrapper<OrderDetail>()
                .eq("order_id", orderId));
        orderVo.setMealList(orderDetails.stream().map(detail -> OrderMealVo.builder()
                .id(detail.getId())
                .mealName(detail.getMealName())
                .mealPic(detail.getMealPic())
                .mealNumber(detail.getMealNumber())
                .unitPrice(AmountUtils.changeF2Y(detail.getUnitPrice()))
                .build()).collect(Collectors.toList()));
        return orderVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void notRefund(OrderBatchValidate validate) {
        List<Order> orders = this.orderMapper.selectList(new QueryWrapper<Order>()
                .in("id", validate.getOrderIds()));
        orders.forEach(order -> {
            Assert.isTrue(OrderStatusEnum.PAID.getCode().equals(order.getStatus()),
                    "订单[" + order.getOrderNo() + "]状态为[" + OrderStatusEnum.getValueByCode(order.getStatus()) + "]不能变更为截止退款...");

            order.setStatus(OrderStatusEnum.DURING_MEAL.getCode());
            order.setUpdateTime(TimeUtils.timestamp());
            orderMapper.updateById(order);
        });
    }

    /**
     * 一键配送
     */
    @Override
    public void allDelivering() {
        List<Integer> statusList = Arrays.asList(OrderStatusEnum.PAID.getCode(),
                OrderStatusEnum.DURING_MEAL.getCode(),
                OrderStatusEnum.SHIPPING.getCode());
        List<Order> orders = this.orderMapper.selectList(new QueryWrapper<Order>()
                .in("status", statusList));
        orders.forEach(order -> {
            order.setStatus(OrderStatusEnum.SHIPPING.getCode());
            order.setUpdateTime(TimeUtils.timestamp());
            orderMapper.updateById(order);
        });
    }

    /**
     * 一键送达
     * 把订单状态为配送中的订单变更为已送达
     * 由于一期没有评价功能，所以这里变更为已完成状态
     */
    @Override
    public void allDelivered() {
        // 查询配送中的订单
        List<Integer> statusList = Collections.singletonList(OrderStatusEnum.SHIPPING.getCode());
        List<Order> orders = this.orderMapper.selectList(new QueryWrapper<Order>()
                .in("status", statusList));
        // 变更为已完成
        orders.forEach(order -> {
            order.setStatus(OrderStatusEnum.COMPLETED.getCode());
            order.setUpdateTime(TimeUtils.timestamp());
            orderMapper.updateById(order);
        });
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delivering(OrderBatchValidate validate) {
        List<Integer> statusList = Arrays.asList(OrderStatusEnum.PAID.getCode(),
                OrderStatusEnum.DURING_MEAL.getCode());
        List<Order> orders = this.orderMapper.selectList(new QueryWrapper<Order>()
                .in("id", validate.getOrderIds()));
        orders.forEach(order -> {
            Assert.isTrue(statusList.contains(order.getStatus()),
                    "订单[" + order.getOrderNo() + "]状态为[" + OrderStatusEnum.getValueByCode(order.getStatus()) + "]不能变更为配送中...");
            order.setStatus(OrderStatusEnum.SHIPPING.getCode());
            order.setUpdateTime(TimeUtils.timestamp());
            orderMapper.updateById(order);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delivered(OrderBatchValidate validate) {
        List<Order> orders = this.orderMapper.selectList(new QueryWrapper<Order>()
                .in("id", validate.getOrderIds()));
        List<Integer> statusList = Arrays.asList(OrderStatusEnum.PAID.getCode(),
                OrderStatusEnum.DURING_MEAL.getCode(),
                OrderStatusEnum.SHIPPING.getCode());
        Long timestamp = TimeUtils.timestamp();
        orders.forEach(order -> {
            Assert.isTrue(statusList.contains(order.getStatus()),
                    "订单[" + order.getOrderNo() + "]状态为[" + OrderStatusEnum.getValueByCode(order.getStatus()) + "]不能变更为已完成...");
            // TODO 由于第一期没有评价功能，所以配送完成后，直接变更为已完成
            order.setStatus(OrderStatusEnum.COMPLETED.getCode());
            order.setDeliveryTime(timestamp);
            order.setUpdateTime(timestamp);
            orderMapper.updateById(order);
        });
    }

    @Transactional(rollbackFor = Exception.class)
    public void refund(OrderBatchValidate validate) {
        Integer adminId = LikeAdminThreadLocal.getAdminId();
        logger.info("管理员[{}]对订单[{}]进行了退款操作", adminId, validate.getOrderIds());
        List<Order> orders = this.orderMapper.selectList(new QueryWrapper<Order>()
                .in("id", validate.getOrderIds()));
        Long timestamp = TimeUtils.timestamp();
        orders.forEach(order -> {
            if (order.getActualAmount() > 0) {
                order.setStatus(OrderStatusEnum.APPLY_REFUNDED.getCode());
                order.setApplyRefundTime(timestamp);
                order.setRefundTime(timestamp);
                order.setUpdateTime(timestamp);
                this.orderMapper.updateById(order);
                if (order.getActualAmount() > 0) {
                    Refund refund = this.wxApplyRefund(order);
                    logger.debug("订单号[{}]申请微信退款返回:[{}]", order.getOrderNo(), refund);
                    Assert.notNull(refund, "调用微信退款失败");
                    if (refund.getStatus().equals(Status.SUCCESS)) {
                        try {
                            Date createDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(refund.getCreateTime());
                            Date successDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(refund.getSuccessTime());
                            refundSuccess(refund.getAmount().getRefund().intValue(), createDate, successDate,
                                    refund.getOutTradeNo(), refund.getTransactionId(), refund.getOutRefundNo(),
                                    refund.getRefundId(), refund.getUserReceivedAccount(), order);
                        } catch (Exception e) {
                            throw new RuntimeException("时间转换异常！");
                        }
                    }
                }
            } else {
                order.setStatus(OrderStatusEnum.REFUNDED.getCode());
                order.setApplyRefundTime(timestamp);
                order.setRefundTime(timestamp);
                order.setUpdateTime(timestamp);
                this.orderMapper.updateById(order);
                Date date = new Date();
                refundSuccess(order.getActualAmount(), date, date,
                        order.getOrderNo(), null, null,
                        null, null, order);
            }
        });
    }

    @Override
    public void export(String date, HttpServletResponse response) {
        // 菜单套餐列表
        List<MenuMeal> meals = getMeal(date);
        Assert.notEmpty(meals, "未查询到当天的套餐信息！");
        List<Integer> mealIds = meals.stream().map(MenuMeal::getMealId).collect(Collectors.toList());
        List<OrderMeal> mealList = orderMealMapper.selectBatchIds(mealIds);
        // 大厦列表
        List<Mansion> mansionList = mansionMapper.selectList(new QueryWrapper<Mansion>()
                .eq("is_delete", 0));
        // 获取订单列表
        List<Order> orders = getOrder(date);
        List<Integer> orderIds = orders.stream().map(Order::getId).collect(Collectors.toList());
        // 获取订单详情列表
        List<OrderDetail> orderDetails = new ArrayList<>(orderIds.size());
        Map<Integer, List<Address>> addressMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(orderIds)) {
            orderDetails = getOrderDetail(orderIds);
            // 获取地址列表
            List<Integer> addressIds = orders.stream().map(Order::getAddressId).collect(Collectors.toList());
            List<Address> addressList = addressMapper.selectBatchIds(addressIds);
            addressMap = addressList.stream().collect(Collectors.groupingBy(Address::getMansionId));
        }

        try {
            XSSFWorkbook workbook = new XSSFWorkbook();
            XSSFSheet sheet = workbook.createSheet("总表");
            //设置单元格居中
            XSSFCellStyle cellStyle = getCellStyleByColor(workbook, IndexedColors.WHITE.index);
            XSSFCellStyle greyStyle = getCellStyleByColor(workbook, IndexedColors.GREY_25_PERCENT.index);

            setTotal(cellStyle, greyStyle, sheet, mealList, addressMap, orders, orderDetails, mansionList);

            for (Mansion mansion : mansionList) {
                XSSFSheet mansionSheet = workbook.createSheet(mansion.getName());
                setData(cellStyle, greyStyle, mansionSheet, mealList, addressMap, orders, orderDetails, mansion);
            }
            String fileName = URLEncoder.encode("牛大师团餐-" + date, "utf-8");
            //设置response
//            response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xlsx");
//            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
//            response.setContentType("application/octet-stream;charset=UTF-8");
            //设置编码格式
            response.setCharacterEncoding("GBK");
            //将表格内容写到输出流中并刷新缓存
            @Cleanup ByteArrayOutputStream os = new ByteArrayOutputStream();
            workbook.write(os);
            byte[] data = os.toByteArray();
            /*@Cleanup ServletOutputStream out = null;
            out = response.getOutputStream();
            workbook.write(out);
            out.flush();*/
            os.flush();
            workbook.close();
            response.reset();
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
            response.addHeader("Content-Length", "" + data.length);
            response.setContentType("application/octet-stream; charset=UTF-8");
            IOUtils.write(data, response.getOutputStream());
        } catch (Exception e) {
            logger.error("生成excel报错", e);
        }

    }

    private XSSFCellStyle getCellStyleByColor(XSSFWorkbook workbook, short color) {
        XSSFCellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        //设置背景色
        cellStyle.setFillForegroundColor(color);
        //必须设置 否则背景色不生效
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        return cellStyle;
    }


    @Override
    public void fallbackCoupon() {
        final List<Order> orderList = orderMapper.selectList(new QueryWrapper<Order>()
                .eq("status", OrderStatusEnum.ORDERED.getCode()));
        Long timestamp = TimeUtils.timestamp();
        for (Order order : orderList) {
            if (timestamp - order.getCreateTime() < 300) {
                continue;
            }
            // 如果使用优惠券回退
            if (order.getCouponId() != null && order.getCouponId() != 0) {
                Coupon coupon = couponMapper.selectById(order.getCouponId());
                coupon.setStatus(0);
                coupon.setUseTime(null);
                couponMapper.updateById(coupon);
            }
            // 恢复库存
            List<OrderDetail> detailList = orderDetailMapper.selectList(
                    new QueryWrapper<OrderDetail>()
                            .eq("order_id", order.getId()));
            detailList.forEach(detail -> {
                MenuMeal menuMeal = this.menuMealMapper.selectById(detail.getMenuMealId());
                menuMeal.setStock(menuMeal.getStock() + detail.getMealNumber());
                menuMeal.setSalesNum(menuMeal.getSalesNum() - detail.getMealNumber());
                menuMeal.setUpdateTime(timestamp);
                this.menuMealMapper.updateById(menuMeal);
            });

            order.setStatus(-1);
            order.setUpdateTime(timestamp);
            orderMapper.updateById(order);
        }

    }

    private Refund wxApplyRefund(Order order) {
        CreateRequest createRequest = new CreateRequest();
        createRequest.setOutTradeNo(order.getOrderNo());
        createRequest.setOutRefundNo(OrderUtils.getInstance().generateOrder(CodeEnum.REFUND_ORDER, order.getUserId()));
        createRequest.setTransactionId(order.getTransactionId());
        AmountReq amountReq = new AmountReq();
        amountReq.setRefund(order.getActualAmount().longValue());
        amountReq.setTotal(order.getActualAmount().longValue());
        amountReq.setCurrency("CNY");
        createRequest.setAmount(amountReq);
        return WxPayUtils.applyRefund(createRequest);
    }

    private void refundSuccess(Integer amount, Date createTime, Date successTime,
                               String orderNo, String transactionId, String refundNo,
                               String refundId, String userReceivedAccount, Order order) {
        // 退款表
        com.mdd.common.entity.order.Refund refund = com.mdd.common.entity.order.Refund
                .builder()
                .orderId(order.getId())
                .orderNo(orderNo)
                .transactionId(transactionId)
                .refundNo(refundNo)
                .refundAmount(order.getActualAmount())
                .refundActualAmount(amount)
                .wxRefundId(refundId)
                .status(1)
                .userReceivedAccount(userReceivedAccount)
                .build();
        if (createTime != null) {
            refund.setRefundTime(createTime.getTime());
        }
        if (successTime != null) {
            refund.setRefundTime(successTime.getTime());
        }
        this.refundMapper.insert(refund);
        // 如果使用优惠券回退
        if (order.getCouponId() != null && order.getCouponId() != 0) {
            Coupon coupon = couponMapper.selectById(order.getCouponId());
            coupon.setStatus(0);
            coupon.setUseTime(null);
            couponMapper.updateById(coupon);
        }
        // 恢复库存
        List<OrderDetail> detailList = orderDetailMapper.selectList(
                new QueryWrapper<OrderDetail>()
                        .eq("order_id", order.getId()));
        detailList.forEach(detail -> {
            MenuMeal menuMeal = this.menuMealMapper.selectById(detail.getMenuMealId());
            menuMeal.setStock(menuMeal.getStock() + detail.getMealNumber());
            menuMeal.setSalesNum(menuMeal.getSalesNum() - detail.getMealNumber());
            menuMeal.setUpdateTime(TimeUtils.timestamp());
            this.menuMealMapper.updateById(menuMeal);
        });
    }

    private List<MenuMeal> getMeal(String date) {
        OrderMenu menu = orderMenuMapper.selectOne(new QueryWrapper<OrderMenu>()
                .eq("day", LocalDate.parse(date))
                .last("limit 1"));
        if (menu != null) {
            return menuMealMapper.selectList(new QueryWrapper<MenuMeal>()
                    .eq("menu_id", menu.getId()));
        } else {
            return null;
        }
    }

    private List<Order> getOrder(String date) {
        Long now = TimeUtils.timestamp();
        OrderMenu menu = this.orderMenuMapper.selectOne(new QueryWrapper<OrderMenu>()
                .eq("day", date)
                .last("limit 1"));
        if (menu == null) {
            return Lists.newArrayList();
        }
        List<OrderDetail> details = orderDetailMapper.selectList(new QueryWrapper<OrderDetail>()
                .eq("menu_id", menu.getId()));
        if (details.isEmpty()) {
            return Lists.newArrayList();
        }
        Set<Integer> orderIds = details.stream().map(OrderDetail::getOrderId).collect(Collectors.toSet());
        return orderMapper.selectList(new QueryWrapper<Order>()
                .ne("status", OrderStatusEnum.EXPIRED.getCode())
                .ne("status", OrderStatusEnum.ORDERED.getCode())
                .ne("status", OrderStatusEnum.APPLY_REFUNDED.getCode())
                .ne("status", OrderStatusEnum.REFUNDED.getCode())
                .in("id", orderIds));
    }

    private List<OrderDetail> getOrderDetail(List<Integer> orderIds) {
        return orderDetailMapper.selectList(new QueryWrapper<OrderDetail>()
                .in("order_id", orderIds));
    }

    private void setTotal(XSSFCellStyle cellStyle, XSSFCellStyle greyStyle, XSSFSheet sheet, List<OrderMeal> mealList,
                          Map<Integer, List<Address>> addressMap, List<Order> orders,
                          List<OrderDetail> orderDetails, List<Mansion> mansionList) {
        // 表头
        List<String> mealNameList = mealList.stream().map(OrderMeal::getMealName).collect(Collectors.toList());
        List<Integer> mealIdList = mealList.stream().map(OrderMeal::getId).collect(Collectors.toList());
        XSSFRow rowTitle = sheet.createRow(0);
        XSSFCell titleRowCell = rowTitle.createCell(0);
        titleRowCell.setCellValue("牛大师团餐-总表");
        // 日期
        XSSFRow rowDate = sheet.createRow(1);
        XSSFCell cellDate = rowDate.createCell(0);
        cellDate.setCellValue("日期");
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        XSSFCell cellDate1 = rowDate.createCell(2);
        cellDate1.setCellValue(format.format(new Date()));
        // 套餐
        XSSFRow rowMeal = sheet.createRow(2);
        XSSFCell cellMeal = rowMeal.createCell(0);
        cellMeal.setCellValue("大厦名称");
        XSSFCell totalCell = rowMeal.createCell(1);
        totalCell.setCellValue("总和");
        rowDate.createCell(1);
        rowTitle.createCell(1);
        for (int i = 0; i < mealNameList.size(); i++) {
            String name = mealNameList.get(i);
            XSSFCell mealCell = rowMeal.createCell(i + 2);
            rowTitle.createCell(i + 2);
            if (rowDate.getCell(i + 2) == null) {
                rowDate.createCell(i + 2);
            }
            mealCell.setCellValue(name);
        }
        // 合并单元格
        CellRangeAddress region = new CellRangeAddress(0, 0, 0, mealList.size() + 1);
        sheet.addMergedRegion(region);
        // 数据
        Map<Integer, Integer> totalMap = new HashMap<>();
        for (int i = 0; i < mansionList.size(); i++) {
            XSSFRow rowMansion = sheet.createRow(i + 5);
            Mansion mansion = mansionList.get(i);
            XSSFCell cellMansion = rowMansion.createCell(0);
            cellMansion.setCellValue(mansion.getName());
            Map<Integer, List<OrderDetail>> countMap = this.getCount(addressMap, orders, orderDetails, mansion.getId());
            //各个点总计
            int mansionTotalCount = 0;
            XSSFCell mansionTotalCell = rowMansion.createCell(1);
            for (int j = 0; j < mealList.size(); j++) {
                OrderMeal meal = mealList.get(j);
                XSSFCell cell = rowMansion.createCell(j + 2);
                int count = CollectionUtils.isNotEmpty(countMap.get(meal.getId())) ? countMap.get(meal.getId()).stream().mapToInt(OrderDetail::getMealNumber).sum() : 0;
                mansionTotalCount += count;
                Integer total = 0;
                if (totalMap.containsKey(meal.getId())) {
                    total = totalMap.get(meal.getId());
                }
                totalMap.put(meal.getId(), total + count);
                if (count > 0) {
                    cell.setCellValue(count);
                }
            }
            if (mansionTotalCount > 0) {
                mansionTotalCell.setCellValue(mansionTotalCount);
            }
        }
        // 总计
        XSSFRow totalRow = sheet.createRow(3);
        XSSFCell totalRowCell = totalRow.createCell(0);
        totalRow.createCell(1);
        int total = totalMap.values().stream().mapToInt(i -> i).sum();
        totalRowCell.setCellValue("套餐总数（" + total + "）");
        for (int i = 0; i < mealIdList.size(); i++) {
            Integer id = mealIdList.get(i);
            Integer num = totalMap.get(id);
            XSSFCell cell = totalRow.createCell(i + 2);
            cell.setCellValue(num);
        }
        // 是否分拣完成
        XSSFRow endRow = sheet.createRow(4);
        XSSFCell endCell = endRow.createCell(0);
        endCell.setCellValue("是否分拣完成");
        endRow.createCell(1);
        for (int i = 0; i < mealIdList.size(); i++) {
            endRow.createCell(i + 2);
        }
        //自适应
        //获取当前列的宽度，然后对比本列的长度，取最大值
        try {
            for (int columnNum = 0; columnNum <= mealList.size() + 1; columnNum++) {
                int columnWidth = sheet.getColumnWidth(columnNum) / 256;
                for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
                    Row currentRow = sheet.getRow(rowNum);
                    if (currentRow.getCell(columnNum) != null) {
                        Cell currentCell = currentRow.getCell(columnNum);
                        int length = 0;
                        length = currentCell.toString().getBytes("GBK").length;
                        if (columnWidth < length + 1) {
                            columnWidth = length + 1;
                        }
                    }
                }
                sheet.setColumnWidth(columnNum, columnWidth * 256);
            }

        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        for (Row row : sheet) {
            row.setHeightInPoints(20f);
            boolean withe = true;
            if (row.getRowNum() % 2 != 0) {
                withe = false;
            }
            for (Cell cell : row) {
                cell.setCellStyle(withe ? cellStyle : greyStyle);
            }
        }

    }

    private Map<Integer, List<OrderDetail>> getCount(Map<Integer, List<Address>> addressMap, List<Order> orders,
                                                     List<OrderDetail> orderDetails, Integer mansionId) {
        Map<Integer, List<OrderDetail>> resultMap = new HashMap<>(0);
        List<Address> addressList = addressMap.get(mansionId);
        if (CollectionUtils.isEmpty(addressList)) {
            return resultMap;
        }
        List<Integer> addressIds = addressList.stream().map(Address::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(orders)) {
            return resultMap;
        }
        List<Order> orderList = orders.stream().filter(order -> addressIds.contains(order.getAddressId()))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(orderList)) {
            List<Integer> orderIds = orderList.stream().map(Order::getId).collect(Collectors.toList());
            List<OrderDetail> detailList = orderDetails.stream()
                    .filter(detail -> orderIds.contains(detail.getOrderId()))
                    .collect(Collectors.toList());
            resultMap = detailList.stream().collect(Collectors.groupingBy(OrderDetail::getMealId));
        }
        return resultMap;

    }

    private void setData(XSSFCellStyle cellStyle, XSSFCellStyle greyStyle, XSSFSheet sheet, List<OrderMeal> mealList,
                         Map<Integer, List<Address>> addressMap, List<Order> orders,
                         List<OrderDetail> orderDetails, Mansion mansion) {
        // 表头
        List<String> mealNameList = mealList.stream().map(OrderMeal::getMealName).collect(Collectors.toList());
        List<Integer> mealIdList = mealList.stream().map(OrderMeal::getId).collect(Collectors.toList());
        XSSFRow rowTitle = sheet.createRow(0);
        XSSFCell titleRowCell = rowTitle.createCell(0);
        titleRowCell.setCellValue("牛大师团餐-" + mansion.getName());
        // 日期
        XSSFRow rowDate = sheet.createRow(1);
        XSSFCell cellDate = rowDate.createCell(0);
        cellDate.setCellValue("日期");
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        XSSFCell cellDate1 = rowDate.createCell(1);
        cellDate1.setCellValue(format.format(new Date()));
        XSSFCell extensionCell = rowDate.createCell(2);
        extensionCell.setCellValue("推广人员");
        rowDate.createCell(3);
        XSSFCell distributionCell = rowDate.createCell(4);
        distributionCell.setCellValue("配送人员");
        rowDate.createCell(5);
        // 套餐
        XSSFRow rowMeal = sheet.createRow(2);
        XSSFCell cellFloor = rowMeal.createCell(0);
        cellFloor.setCellValue("楼层");
        XSSFCell cellRoom = rowMeal.createCell(1);
        cellRoom.setCellValue("房间号");
        for (int i = 0; i < Math.max(mealNameList.size(), 5); i++) {
            String name = mealNameList.size() > i ? mealNameList.get(i) : "";
            XSSFCell mealCell = rowMeal.createCell(i + 2);
            rowTitle.createCell(i + 2);
            if (rowDate.getCell(i + 2) == null) {
                rowDate.createCell(i + 2);
            }
            if (StringUtils.isNotBlank(name)) {
                mealCell.setCellValue(name);
            }
        }

        // 合并单元格
        CellRangeAddress region = new CellRangeAddress(0, 0, 0, Math.max(mealNameList.size() + 1, 6));
        sheet.addMergedRegion(region);
        // 数据
        List<Address> addressList = addressMap.get(mansion.getId());
        List<Address> distinctList = new ArrayList<>();
        Map<Integer, Integer> totalMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(addressList)) {
            distinctList = addressList.stream()
                    .collect(Collectors.toMap(Address::getHouseNumber, Function.identity(), (a1, a2) -> a1))
                    .values().stream().sorted(Comparator.comparing(Address::getFloor)).collect(Collectors.toList());
            for (int i = 0; i < distinctList.size(); i++) {
                Address address = distinctList.get(i);
                XSSFRow row = sheet.createRow(i + 5);
                // 设置楼层
                XSSFCell rowCell = row.createCell(0);
                rowCell.setCellValue(address.getFloor());
                // 设置房间号
                XSSFCell rowHouse = row.createCell(1);
                rowHouse.setCellValue(address.getHouseNumber());
                Map<Integer, List<OrderDetail>> countMap = getCountByFloor(addressMap, orders, orderDetails, mansion.getId(), address.getHouseNumber());
                for (int j = 0; j < Math.max(mealList.size(), 5); j++) {
                    OrderMeal meal = mealList.size() > j ? mealList.get(j) : new OrderMeal();
                    XSSFCell cell = row.createCell(j + 2);
                    int count = CollectionUtils.isNotEmpty(countMap.get(meal.getId())) ? countMap.get(meal.getId()).stream().mapToInt(OrderDetail::getMealNumber).sum() : 0;
                    Integer total = 0;
                    if (totalMap.containsKey(meal.getId())) {
                        total = totalMap.get(meal.getId());
                    }
                    totalMap.put(meal.getId(), total + count);
                    if (count > 0) {
                        cell.setCellValue(count);
                    }
                }
            }
        }
        // 总计
        XSSFRow totalRow = sheet.createRow(3);
        XSSFCell totalRowCell = totalRow.createCell(0);
        int total = totalMap.entrySet().stream().mapToInt((s) -> s.getValue()).sum();
        totalRowCell.setCellValue("套餐总数（" + total + "）");
        totalRow.createCell(1).setCellValue("");
        for (int i = 0; i < Math.max(mealIdList.size(), 5); i++) {
            XSSFCell cell = totalRow.createCell(i + 2);
            if (mealIdList.size() > i) {
                Integer id = mealIdList.get(i);
                Integer num = totalMap.get(id);
                if (num != null) {
                    cell.setCellValue(num);
                }
            } else {
                cell.setCellValue("");
            }
        }
        // 是否分拣完成
        XSSFRow endRow = sheet.createRow(4);
        endRow.createCell(0).setCellValue("是否分拣完成");
        endRow.createCell(1).setCellValue("");
        for (int i = 0; i < Math.max(mealIdList.size(), 5); i++) {
            endRow.createCell(i + 2);
        }
        // 自适应
        //获取当前列的宽度，然后对比本列的长度，取最大值
        try {
            for (int columnNum = 0; columnNum <= mealList.size() + 1; columnNum++) {
                int columnWidth = sheet.getColumnWidth(columnNum) / 256;
                for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
                    Row currentRow = sheet.getRow(rowNum);
                    if (currentRow.getCell(columnNum) != null) {
                        Cell currentCell = currentRow.getCell(columnNum);
                        int length = 0;
                        length = currentCell.toString().getBytes("GBK").length;
                        if (columnWidth < length + 1) {
                            columnWidth = length + 1;
                        }
                    }
                }
                sheet.setColumnWidth(columnNum, columnWidth * 256);
            }
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        for (Row row : sheet) {
            row.setHeightInPoints(20f);
            boolean withe = true;
            if (row.getRowNum() % 2 != 0) {
                withe = false;
            }
            for (Cell cell : row) {
                cell.setCellStyle(withe ? cellStyle : greyStyle);
            }
        }
    }

    private Map<Integer, List<OrderDetail>> getCountByFloor(Map<Integer, List<Address>> addressMap, List<Order> orders,
                                                            List<OrderDetail> orderDetails, Integer mansionId, String houseNumber) {
        Map<Integer, List<OrderDetail>> resultMap = new HashMap<>(0);
        List<Address> addressList = addressMap.get(mansionId);
        if (CollectionUtils.isEmpty(addressList)) {
            return resultMap;
        }
        List<Address> addresses = addressList.stream().filter(address -> address.getHouseNumber().equals(houseNumber)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(orders)) {
            return resultMap;
        }
        List<Integer> addressIds = addresses.stream().map(Address::getId).distinct().collect(Collectors.toList());
        List<Order> orderList = orders.stream().filter(order -> addressIds.contains(order.getAddressId()))
                .collect(Collectors.toList());
        List<OrderDetail> detailList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(orderList)) {
            List<Integer> orderIds = orderList.stream().map(Order::getId).collect(Collectors.toList());
            detailList = orderDetails.stream()
                    .filter(detail -> orderIds.contains(detail.getOrderId()))
                    .collect(Collectors.toList());
        }
        return detailList.stream().collect(Collectors.groupingBy(OrderDetail::getMealId));
    }
}
